﻿#region License

// Created by John Batte on 04/03/2012 at 3:00 PM.
// Last code cleanup: 04/06/2012 at 6:31 AM.
// 
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
// 
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.
// 
// You should have received a copy of the GNU General Public License
// along with this program.  If not, see <http://www.gnu.org/licenses/>.

#endregion

using System;
using System.Collections.Generic;
using System.IO;
using System.Reflection;
using System.Threading.Tasks;
using System.Xml.Linq;

using BatteCode.Fx.ExceptionHandling;
using BatteCode.Google.Wiki.Templates;

namespace BatteCode.Google.Wiki
{
	/// <summary>
	/// 	Provides a wiki generator.
	/// </summary>
	public class WikiGenerator
	{
		private readonly IWikiScanner _scanner;
		private readonly IWikiWriter _writer;

		/// <summary>
		/// 	Initializes a new instance of the <see cref="WikiGenerator" /> class.
		/// </summary>
		/// <param name="scanner"> The scanner. </param>
		/// <param name="writer"> The writer. </param>
		public WikiGenerator(IWikiScanner scanner, IWikiWriter writer)
		{
			_scanner = scanner;
			_writer = writer;
		}

		/// <summary>
		/// 	Generates wiki entries using the specified targets.
		/// </summary>
		/// <param name="targets"> The targets. </param>
		/// <param name="outputDirectory"> The output directory. </param>
		/// <param name="statusOutput"> The status output writer. </param>
		/// <returns> </returns>
		public IObservable<WikiStatus> Generate(IEnumerable<WikiTarget> targets, string outputDirectory, TextWriter statusOutput = null)
		{
			Parallel.ForEach(targets, target =>
			{
				Type[] types = Try.Get(target.Assembly.GetTypes);
				if (types == null || types.Length == 0) return;
				XElement comments = target.CommentFile != null && target.CommentFile.Exists
				                    	? XElement.Load(target.CommentFile.FullName)
				                    	: null;

				Parallel.ForEach(types, type =>
				{
					_writer.Write(type, TransformType(type, comments));
					IEnumerable<MemberInfo> members = _scanner.GetWikiMembers(type);
					if (members == null) return;

					Parallel.ForEach(members, member => _writer.Write(member, TransformMember(member, type, comments)));
				});
			});

			return _writer.StatusUpdates;
		}

		private static string TransformType(Type type, XElement comments)
		{
			if (type.IsClass) return TransformClass(type, comments);

			if (type.IsEnum) return TransformEnum(type, comments);

			if (type.IsInterface) return TransformInterface(type, comments);

			throw new InvalidOperationException();
		}

		private static string TransformMember(MemberInfo member, Type type, XElement comments)
		{
			switch (member.MemberType)
			{
				case MemberTypes.Event:
					return TransformEvent(member as EventInfo, type, comments);
				case MemberTypes.Property:
					return TransformProperty(member as PropertyInfo, type, comments);
				case MemberTypes.Method:
					return TransformMethod(member as MethodInfo, type, comments);
				case MemberTypes.Field:
					return TransformField(member as FieldInfo, type, comments);
			}

			throw new InvalidOperationException();
		}

		private static string TransformField(FieldInfo field, Type type, XElement comments)
		{
			throw new NotImplementedException();
		}

		private static string TransformMethod(MethodInfo method, Type type, XElement comments)
		{
			throw new NotImplementedException();
		}

		private static string TransformProperty(PropertyInfo property, Type type, XElement comments)
		{
			throw new NotImplementedException();
		}

		private static string TransformEvent(EventInfo @event, Type type, XElement comments)
		{
			throw new NotImplementedException();
		}

		private static string TransformInterface(Type type, XElement comments)
		{
			return new InterfaceTemplate
			{
				InterfaceType = type,
				Comments = comments.GetCommentsForMember(type)
			}.TransformText();
		}

		private static string TransformEnum(Type type, XElement comments)
		{
			return new EnumTemplate
			{
				EnumType = type,
				Comments = comments.GetCommentsForMember(type)
			}.TransformText();
		}

		private static string TransformClass(Type type, XElement comments)
		{
			return new ClassTemplate
			{
				ClassType = type,
				Comments = comments.GetCommentsForMember(type)
			}.TransformText();
		}
	}
}